#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinterdnd2 import TkinterDnD, DND_FILES
import os
import threading
from pathlib import Path
from PIL import Image, ImageOps
import queue
from typing import List, Optional, Tuple

class ImageCompressorGUI:
    def __init__(self):
        self.root = TkinterDnD.Tk()
        self.root.title("图片压缩工具")
        self.root.geometry("850x780")
        self.root.minsize(800, 780)
        self.root.resizable(True, True)
        
        self.supported_formats = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
        self.max_images = 100
        self.image_files = []
        self.progress_queue = queue.Queue()
        
        self.setup_ui()
        self.setup_drag_drop()
        
    def setup_ui(self):
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        title_label = ttk.Label(main_frame, text="图片压缩工具", font=("Arial", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        upload_frame = ttk.LabelFrame(main_frame, text="上传区域", padding="10")
        upload_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        upload_frame.columnconfigure(0, weight=1)
        
        self.drop_area = tk.Label(upload_frame, 
                                 text="📁 拖拽图片、文件夹到此处 或 点击下方按钮选择",
                                 bg="#f0f0f0", 
                                 relief="groove",
                                 bd=1,
                                 height=2,
                                 font=("Arial", 10),
                                 fg="#666666")
        self.drop_area.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        
        button_frame = ttk.Frame(upload_frame)
        button_frame.grid(row=1, column=0, pady=(0, 10))
        
        ttk.Button(button_frame, text="选择图片文件", 
                  command=self.select_images).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="选择文件夹", 
                  command=self.select_folder).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="清空列表", 
                  command=self.clear_files).pack(side=tk.LEFT, padx=(5, 0))
        
        
        settings_frame = ttk.LabelFrame(main_frame, text="压缩设置", padding="10")
        settings_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        settings_frame.columnconfigure(1, weight=1)
        
        ttk.Label(settings_frame, text="压缩质量:").grid(row=0, column=0, sticky=tk.W, pady=(0, 10))
        
        quality_frame = ttk.Frame(settings_frame)
        quality_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(0, 10))
        quality_frame.columnconfigure(1, weight=1)
        
        self.quality_var = tk.IntVar(value=85)
        
        quality_info_frame = ttk.Frame(quality_frame)
        quality_info_frame.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 5))
        quality_info_frame.columnconfigure(1, weight=1)
        
        ttk.Label(quality_info_frame, text="高压缩", foreground="#888888", font=("Arial", 8)).grid(row=0, column=0, sticky=tk.W)
        self.quality_label_var = tk.StringVar(value="质量: 85%")
        ttk.Label(quality_info_frame, textvariable=self.quality_label_var, 
                 font=("Arial", 10, "bold"), foreground="#2d5a2d").grid(row=0, column=1, pady=(0, 0))
        ttk.Label(quality_info_frame, text="低压缩", foreground="#888888", font=("Arial", 8)).grid(row=0, column=2, sticky=tk.E)
        
        scale_frame = ttk.Frame(quality_frame)
        scale_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 5))
        scale_frame.columnconfigure(1, weight=1)
        
        ttk.Label(scale_frame, text="5%", font=("Arial", 8), foreground="#666").grid(row=0, column=0, sticky=tk.W)
        self.quality_scale = ttk.Scale(scale_frame, from_=5, to=95, orient=tk.HORIZONTAL, 
                                      variable=self.quality_var, length=350)
        self.quality_scale.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(8, 8))
        ttk.Label(scale_frame, text="95%", font=("Arial", 8), foreground="#666").grid(row=0, column=2, sticky=tk.E)
        
        
        self.quality_scale.configure(command=self.update_quality_label)
        
        ttk.Label(settings_frame, text="输出格式:").grid(row=2, column=0, sticky=tk.W, pady=(10, 5))
        
        format_frame = ttk.Frame(settings_frame)
        format_frame.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 5))
        
        self.format_var = tk.StringVar(value="保持原格式")
        format_options = [
            ("保持原格式 (推荐)", "保持原格式"),
            ("JPEG (文件最小,有损压缩)", "JPEG"),
            ("PNG (质量最高,文件较大)", "PNG"),
            ("WebP (平衡选择,现代格式)", "WebP")
        ]
        
        for i, (text, value) in enumerate(format_options):
            ttk.Radiobutton(format_frame, text=text, variable=self.format_var, 
                           value=value).grid(row=i//2, column=i%2, sticky=tk.W, padx=(0, 20), pady=2)
        
        ttk.Label(settings_frame, text="文件大小阈值:").grid(row=3, column=0, sticky=tk.W, pady=(10, 5))
        
        threshold_frame = ttk.Frame(settings_frame)
        threshold_frame.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 5))
        
        self.threshold_var = tk.IntVar(value=100)
        ttk.Label(threshold_frame, text="小于").pack(side=tk.LEFT)
        threshold_spinbox = ttk.Spinbox(threshold_frame, from_=0, to=5000, width=8, 
                                       textvariable=self.threshold_var)
        threshold_spinbox.pack(side=tk.LEFT, padx=(5, 5))
        ttk.Label(threshold_frame, text="KB 的图片不进行压缩（直接复制）").pack(side=tk.LEFT)
        
        ttk.Label(settings_frame, text="目标文件大小:").grid(row=4, column=0, sticky=tk.W, pady=(10, 5))
        
        target_size_frame = ttk.Frame(settings_frame)
        target_size_frame.grid(row=4, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 5))
        
        self.target_size_var = tk.IntVar(value=500)
        ttk.Label(target_size_frame, text="最大").pack(side=tk.LEFT)
        target_size_spinbox = ttk.Spinbox(target_size_frame, from_=100, to=5000, width=8, 
                                         textvariable=self.target_size_var)
        target_size_spinbox.pack(side=tk.LEFT, padx=(5, 5))
        ttk.Label(target_size_frame, text="KB (适合平台上传限制)").pack(side=tk.LEFT)
        
        ttk.Label(settings_frame, text="压缩模式:").grid(row=5, column=0, sticky=tk.W, pady=(10, 0))
        
        mode_frame = ttk.Frame(settings_frame)
        mode_frame.grid(row=5, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
        
        self.mode_var = tk.StringVar(value="批量")
        mode_options = [
            ("单张压缩", "单张"),
            ("批量压缩到compress文件夹", "批量"),
            ("文件夹递归压缩", "文件夹")
        ]
        
        for i, (text, value) in enumerate(mode_options):
            ttk.Radiobutton(mode_frame, text=text, variable=self.mode_var, 
                           value=value).grid(row=0, column=i, sticky=tk.W, padx=(0, 20))
        
        progress_frame = ttk.LabelFrame(main_frame, text="压缩进度", padding="10")
        progress_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        progress_frame.columnconfigure(0, weight=1)
        
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var, 
                                           maximum=100, length=400)
        self.progress_bar.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
        
        self.status_var = tk.StringVar(value="等待上传图片...")
        ttk.Label(progress_frame, textvariable=self.status_var).grid(row=1, column=0, sticky=tk.W)
        
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=4, column=0, columnspan=3, pady=(0, 10))
        
        self.compress_button = ttk.Button(button_frame, text="开始压缩", 
                                         command=self.start_compression, state="disabled")
        self.compress_button.pack(side=tk.LEFT, padx=(0, 10))
        
        ttk.Button(button_frame, text="退出", command=self.root.quit).pack(side=tk.LEFT)
        
        result_frame = ttk.LabelFrame(main_frame, text="压缩结果", padding="10")
        result_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        result_frame.columnconfigure(0, weight=1)
        main_frame.rowconfigure(5, weight=1)
        
        self.result_text = tk.Text(result_frame, height=6, wrap=tk.NONE, font=("Consolas", 8), state="normal")
        scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.result_text.yview)
        self.result_text.configure(yscrollcommand=scrollbar.set)
        
        h_scrollbar = ttk.Scrollbar(result_frame, orient="horizontal", command=self.result_text.xview)
        self.result_text.configure(xscrollcommand=h_scrollbar.set)
        
        self.result_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        h_scrollbar.grid(row=1, column=0, sticky=(tk.W, tk.E))
        
        result_frame.rowconfigure(0, weight=0)
        result_frame.columnconfigure(0, weight=1)
        
    def setup_drag_drop(self):
        self.drop_area.drop_target_register(DND_FILES)
        self.drop_area.dnd_bind('<<Drop>>', self.on_drop)
        
    def on_drop(self, event):
        files = self.root.tk.splitlist(event.data)
        self.process_dropped_files(files)
        
    def process_dropped_files(self, files):
        self.image_files.clear()
        self.reset_progress()
        
        for file_path in files:
            path = Path(file_path)
            if path.is_file() and self.is_image(path):
                self.image_files.append(str(path))
            elif path.is_dir():
                self.collect_images_from_folder(path)
        
        self.update_file_count()
        self.update_drop_area_text()
        
    def collect_images_from_folder(self, folder_path: Path):
        for item in folder_path.rglob("*"):
            if item.is_file() and self.is_image(item) and len(self.image_files) < self.max_images:
                self.image_files.append(str(item))
                
    def is_image(self, file_path: Path) -> bool:
        return file_path.suffix.lower() in self.supported_formats
        
    def select_images(self):
        files = filedialog.askopenfilenames(
            title="选择图片文件",
            filetypes=[
                ("图片文件", "*.jpg *.jpeg *.png *.bmp *.tiff *.webp"),
                ("所有文件", "*.*")
            ]
        )
        if files:
            self.image_files.clear()
            self.reset_progress()
            self.image_files.extend(files)
            self.update_file_count()
            self.update_drop_area_text()
            
    def select_folder(self):
        folder = filedialog.askdirectory(title="选择文件夹")
        if folder:
            self.image_files.clear()
            self.reset_progress()
            self.collect_images_from_folder(Path(folder))
            self.update_file_count()
            self.update_drop_area_text()
            
    def clear_files(self):
        self.image_files.clear()
        self.reset_progress()
        self.update_file_count()
        self.drop_area.config(text="拖拽图片、文件夹到此处\n或点击选择文件")
        self.result_text.delete(1.0, tk.END)
        
    def reset_progress(self):
        self.progress_var.set(0)
        self.status_var.set("等待上传图片...")
        self.result_text.delete(1.0, tk.END)
        
    def update_file_count(self):
        count = len(self.image_files)
        self.compress_button.config(state="normal" if count > 0 and count <= self.max_images else "disabled")
            
    def update_drop_area_text(self):
        if self.image_files:
            count = len(self.image_files)
            if count > self.max_images:
                text = f"📸 已选择 {count} 张图片 - ⚠️ 超过最大限制({self.max_images}张)"
                self.drop_area.config(bg="#ffe8e8", fg="#d63031")
            else:
                text = f"📸 已选择 {count} 张图片 - 点击'开始压缩'进行处理"
                self.drop_area.config(bg="#e8f5e8", fg="#2d5a2d")
        else:
            text = "📁 拖拽图片、文件夹到此处 或 点击下方按钮选择"
            self.drop_area.config(bg="#f0f0f0", fg="#666666")
        self.drop_area.config(text=text)
        
    def get_quality_value(self):
        return int(self.quality_var.get())
        
    def update_quality_label(self, value):
        quality = int(float(value))
        self.quality_label_var.set(f"质量: {quality}%")
        
        
    def get_format_value(self):
        format_map = {"保持原格式": None, "JPEG": "jpeg", "PNG": "png", "WebP": "webp"}
        return format_map.get(self.format_var.get())
        
    def start_compression(self):
        if not self.image_files:
            messagebox.showwarning("警告", "请先选择图片文件！")
            return
            
        if len(self.image_files) > self.max_images:
            messagebox.showerror("错误", f"图片数量超过限制 ({self.max_images}张)！")
            return
            
        self.compress_button.config(state="disabled")
        self.progress_var.set(0)
        self.result_text.delete(1.0, tk.END)
        
        quality = self.get_quality_value()
        format_change = self.get_format_value()
        mode = self.mode_var.get()
        target_size = self.target_size_var.get()
        
        thread = threading.Thread(target=self.compress_worker, 
                                args=(self.image_files.copy(), quality, format_change, mode, target_size))
        thread.daemon = True
        thread.start()
        
        self.root.after(100, self.update_progress)
        
    def compress_worker(self, image_files: List[str], quality: int, 
                       format_change: Optional[str], mode: str, target_size_kb: int):
        try:
            total_files = len(image_files)
            success_count = 0
            
            if mode == "单张":
                for i, image_path in enumerate(image_files):
                    if self.compress_single_image(image_path, quality, format_change):
                        success_count += 1
                    progress = ((i + 1) / total_files) * 100
                    self.progress_queue.put(("progress", progress, f"处理中... {i+1}/{total_files}"))
                    
            elif mode == "批量":
                compress_folder = self.get_unique_folder_name(Path(image_files[0]).parent)
                compress_folder.mkdir(exist_ok=True)
                
                for i, image_path in enumerate(image_files):
                    if self.compress_batch_image(image_path, compress_folder, quality, format_change):
                        success_count += 1
                    progress = ((i + 1) / total_files) * 100
                    self.progress_queue.put(("progress", progress, f"批量压缩中... {i+1}/{total_files}"))
                    
                self.progress_queue.put(("result", f"批量压缩完成！成功压缩 {success_count} 张图片到 {compress_folder}"))
                
            elif mode == "文件夹":
                success_count, output_folder = self.compress_folder_recursive(image_files, quality, format_change)
                
                if output_folder:
                    self.progress_queue.put(("result", f"\n=== 文件夹递归压缩完成！==="))
                    self.progress_queue.put(("result", f"输出文件夹: {output_folder}"))
                    self.progress_queue.put(("result", f"成功压缩: {success_count}/{total_files} 张图片"))
            
            self.progress_queue.put(("complete", success_count, total_files))
            
        except Exception as e:
            self.progress_queue.put(("error", str(e)))
            
    def compress_single_image(self, image_path: str, quality: int, format_change: Optional[str]) -> bool:
        try:
            input_path = Path(image_path)
            output_path = self.get_unique_name(input_path)
            
            if format_change:
                output_path = output_path.with_suffix(f'.{format_change}')
            
            success, result_msg = self.compress_or_copy_image(input_path, output_path, quality, format_change)
            if success:
                self.progress_queue.put(("result", f"✓ {output_path.name} - {result_msg}"))
            else:
                self.progress_queue.put(("result", f"✗ {output_path.name} - {result_msg}"))
            return success
        except Exception as e:
            self.progress_queue.put(("result", f"✗ {Path(image_path).name}: {e}"))
            return False
            
    def compress_batch_image(self, image_path: str, compress_folder: Path, 
                           quality: int, format_change: Optional[str]) -> bool:
        try:
            input_path = Path(image_path)
            filename = input_path.name
            
            if format_change:
                filename = input_path.stem + f".{format_change.lower()}"
            
            output_path = compress_folder / filename
            
            success, result_msg = self.compress_or_copy_image(input_path, output_path, quality, format_change)
            if success:
                self.progress_queue.put(("result", f"✓ {output_path.name} - {result_msg}"))
            else:
                self.progress_queue.put(("result", f"✗ {output_path.name} - {result_msg}"))
            return success
        except Exception as e:
            self.progress_queue.put(("result", f"✗ {Path(image_path).name}: {e}"))
            return False
            
    def compress_folder_recursive(self, image_files: List[str], quality: int, 
                                format_change: Optional[str]) -> Tuple[int, Path]:
        if not image_files:
            return 0, None
            
        first_file = Path(image_files[0])
        root_folder = self.find_common_root(image_files)
        
        output_folder_name = root_folder.name + "_cp"
        output_folder = self.get_unique_folder_name(root_folder.parent, output_folder_name)
        output_folder.mkdir(exist_ok=True)
        
        success_count = 0
        total_files = len(image_files)
        
        for i, image_path in enumerate(image_files):
            input_path = Path(image_path)
            relative_path = input_path.relative_to(root_folder)
            
            output_subdir = output_folder / relative_path.parent
            output_subdir.mkdir(parents=True, exist_ok=True)
            
            filename = input_path.name
            if format_change:
                filename = input_path.stem + f".{format_change.lower()}"
            
            output_path = output_subdir / filename
            
            success, result_msg = self.compress_or_copy_image(input_path, output_path, quality, format_change)
            if success:
                success_count += 1
                self.progress_queue.put(("result", f"✓ {relative_path} -> {output_path.relative_to(output_folder)} - {result_msg}"))
            else:
                self.progress_queue.put(("result", f"✗ {relative_path}: {result_msg}"))
            
            progress = ((i + 1) / total_files) * 100
            self.progress_queue.put(("progress", progress, f"文件夹递归压缩中... {i+1}/{total_files}"))
        
        return success_count, output_folder
        
    def find_common_root(self, file_paths: List[str]) -> Path:
        if len(file_paths) == 1:
            return Path(file_paths[0]).parent
        
        paths = [Path(p) for p in file_paths]
        common_parts = []
        
        for parts in zip(*[p.parts for p in paths]):
            if len(set(parts)) == 1:
                common_parts.append(parts[0])
            else:
                break
        
        if common_parts:
            return Path(*common_parts)
        else:
            return Path(file_paths[0]).parent
    
    def get_smart_quality(self, file_path: Path, base_quality: int) -> int:
        # 尊重用户设定的质量，只对小文件做适度提升优化
        file_size = file_path.stat().st_size / 1024  # KB
        
        # 对于小文件，可以适度提升质量以获得更好效果
        if file_size < 100 and base_quality < 80:  # 小于100KB且用户设置质量低
            return min(85, base_quality + 15)  # 适度提升
        elif file_size < 500 and base_quality < 70:  # 100KB-500KB且质量低  
            return min(80, base_quality + 10)   # 适度提升
        else:
            # 其他情况一律使用用户设定值
            return base_quality
    
    def compress_to_target_size(self, img: Image.Image, output_path: Path, 
                               target_size_kb: int, format_change: Optional[str], 
                               initial_quality: int = 85, original_size_kb: float = 0) -> Tuple[bool, int, float]:
        """迭代压缩直到满足目标大小要求"""
        import tempfile
        
        # 严格按照用户设定的质量进行压缩
        current_quality = initial_quality
        min_quality = max(5, initial_quality - 10)  # 最低质量限制
        step_size = 5  # 精细控制步长
        
        # 如果没有指定格式且目标很小，自动转换为JPEG获得更好压缩
        auto_format = False
        if not format_change and target_size_kb <= 500:
            format_change = 'jpeg'
            auto_format = True
            if img.mode == 'RGBA':
                background = Image.new('RGB', img.size, (255, 255, 255))
                background.paste(img, mask=img.split()[-1])
                img = background
        
        # 先试试初始质量能否满足要求
        with tempfile.NamedTemporaryFile(delete=False, suffix=f'.{format_change or "jpg"}') as temp_file:
            temp_path = Path(temp_file.name)
        
        try:
            save_kwargs = {'optimize': True}
            if format_change and format_change.lower() in ['jpeg', 'jpg']:
                save_kwargs['quality'] = current_quality
            elif format_change and format_change.lower() == 'webp':
                save_kwargs['quality'] = current_quality
            elif format_change and format_change.lower() == 'png':
                # PNG 使用 compress_level 而不是 quality
                compress_level = min(9, max(0, int((100 - current_quality) / 10)))
                save_kwargs['compress_level'] = compress_level
            
            img.save(temp_path, format=format_change.upper() if format_change else 'JPEG', **save_kwargs)
            temp_size_kb = temp_path.stat().st_size / 1024
            
            # 如果初始质量就满足要求且不比原文件大，直接返回
            if temp_size_kb <= target_size_kb and (original_size_kb == 0 or temp_size_kb <= original_size_kb):
                import shutil
                shutil.move(str(temp_path), str(output_path))
                format_info = f" (自动转{format_change.upper()})" if auto_format else ""
                return True, current_quality, temp_size_kb
            else:
                temp_path.unlink()
        except Exception as e:
            if temp_path.exists():
                temp_path.unlink()
            return False, current_quality, 0
        
        # 需要进一步压缩，逐步降低质量
        while current_quality >= min_quality:
            # 先降低质量
            current_quality -= step_size
            
            # 确保不低于最低限制
            if current_quality < min_quality:
                current_quality = min_quality
            
            with tempfile.NamedTemporaryFile(delete=False, suffix=f'.{format_change or "jpg"}') as temp_file:
                temp_path = Path(temp_file.name)
            
            try:
                save_kwargs = {'optimize': True}
                if format_change and format_change.lower() in ['jpeg', 'jpg']:
                    save_kwargs['quality'] = current_quality
                elif format_change and format_change.lower() == 'webp':
                    save_kwargs['quality'] = current_quality
                elif format_change and format_change.lower() == 'png':
                    # PNG 使用 compress_level 而不是 quality
                    compress_level = min(9, max(0, int((100 - current_quality) / 10)))
                    save_kwargs['compress_level'] = compress_level
                
                img.save(temp_path, format=format_change.upper() if format_change else 'JPEG', **save_kwargs)
                temp_size_kb = temp_path.stat().st_size / 1024
                
                if temp_size_kb <= target_size_kb and (original_size_kb == 0 or temp_size_kb <= original_size_kb):
                    import shutil
                    shutil.move(str(temp_path), str(output_path))
                    return True, current_quality, temp_size_kb
                else:
                    temp_path.unlink()
                    
                # 如果已经是最低质量还不满足，跳出循环
                if current_quality <= min_quality:
                    break
                    
            except Exception as e:
                if temp_path.exists():
                    temp_path.unlink()
                return False, current_quality, 0
        
        # 用户设定质量无法达到目标大小，使用用户最低质量保存
        try:
            save_kwargs = {'optimize': True}
            if format_change and format_change.lower() in ['jpeg', 'jpg']:
                save_kwargs['quality'] = min_quality
            elif format_change and format_change.lower() == 'webp':
                save_kwargs['quality'] = min_quality
            elif format_change and format_change.lower() == 'png':
                # PNG 使用 compress_level 而不是 quality
                compress_level = min(9, max(0, int((100 - min_quality) / 10)))
                save_kwargs['compress_level'] = compress_level
            
            img.save(output_path, format=format_change.upper() if format_change else 'JPEG', **save_kwargs)
            final_size_kb = output_path.stat().st_size / 1024
            
            # 如果压缩后文件比原文件还大，且没有真正的格式转换，返回失败让上层处理
            if original_size_kb > 0 and final_size_kb > original_size_kb:
                # 如果没有真正的格式转换，则返回失败让上层处理
                if not format_change:
                    if output_path.exists():
                        output_path.unlink()
                    return False, min_quality, final_size_kb
                
            return True, min_quality, final_size_kb
        except Exception:
            return False, min_quality, 0

    def compress_or_copy_image(self, input_path: Path, output_path: Path, 
                              base_quality: int, format_change: Optional[str]) -> Tuple[bool, str]:
        try:
            file_size_kb = input_path.stat().st_size / 1024
            threshold_kb = self.threshold_var.get() if hasattr(self, 'threshold_var') else 100
            target_size_kb = self.target_size_var.get() if hasattr(self, 'target_size_var') else 500
            
            # 小于阈值的文件直接复制
            if file_size_kb < threshold_kb:
                import shutil
                shutil.copy2(input_path, output_path)
                return True, f"文件过小({file_size_kb:.1f}KB)，直接复制"
            
            # 如果原文件已经满足目标大小且质量较好，直接复制
            if file_size_kb <= target_size_kb and not format_change:
                import shutil
                shutil.copy2(input_path, output_path)
                return True, f"文件已符合要求({file_size_kb:.1f}KB≤{target_size_kb}KB)，直接复制"
            
            with Image.open(input_path) as img:
                img = ImageOps.exif_transpose(img)
                
                # 尝试迭代压缩到目标大小
                success, final_quality, output_size_kb = self.compress_to_target_size(
                    img, output_path, target_size_kb, format_change, base_quality, file_size_kb
                )
                
                if success:
                    # 检查是否真的有格式转换
                    original_ext = input_path.suffix.lower().lstrip('.')
                    # 将jpeg和jpg视为相同格式
                    if original_ext == 'jpeg':
                        original_ext = 'jpg'
                    target_ext = format_change.lower() if format_change else original_ext
                    if target_ext == 'jpeg':
                        target_ext = 'jpg'
                    is_real_format_change = format_change and original_ext != target_ext
                    
                    # 如果压缩后文件比原文件还大，且没有真正的格式转换，直接复制原文件
                    if output_size_kb > file_size_kb and not is_real_format_change:
                        # 删除已生成的压缩文件
                        if output_path.exists():
                            output_path.unlink()
                        import shutil
                        shutil.copy2(input_path, output_path)
                        return True, f"✓ 压缩后文件更大，复制原文件 ({file_size_kb:.1f}KB)"
                    elif output_size_kb > file_size_kb and is_real_format_change:
                        # 真正的格式转换导致文件变大时，给出警告但仍保存
                        return True, f"⚠ {file_size_kb:.1f}KB→{output_size_kb:.1f}KB (质量:{final_quality}% 格式转换导致文件变大)"
                    
                    compression_ratio = (1 - output_size_kb / file_size_kb) * 100
                    if output_size_kb <= target_size_kb:
                        status = "✓"
                        size_info = ""
                    else:
                        status = "⚠"
                        size_info = f" 超出目标{target_size_kb}KB"
                    return True, f"{status} {file_size_kb:.1f}KB→{output_size_kb:.1f}KB (质量:{final_quality}%{size_info})"
                else:
                    return False, "压缩失败"
                    
        except Exception as e:
            return False, f"压缩失败: {str(e)}"

    def compress_image(self, input_path: Path, output_path: Path, quality: int, 
                      format_change: Optional[str]) -> bool:
        success, _ = self.compress_or_copy_image(input_path, output_path, quality, format_change)
        return success
    
    def get_unique_name(self, base_path: Path, suffix: str = "_cp") -> Path:
        stem = base_path.stem
        parent = base_path.parent
        extension = base_path.suffix
        
        new_name = f"{stem}{suffix}{extension}"
        new_path = parent / new_name
        
        counter = 1
        while new_path.exists():
            new_name = f"{stem}{suffix}_{counter}{extension}"
            new_path = parent / new_name
            counter += 1
        
        return new_path
    
    def get_unique_folder_name(self, base_path: Path, folder_name: str = "compress") -> Path:
        folder_path = base_path / folder_name
        counter = 1
        
        while folder_path.exists():
            folder_path = base_path / f"{folder_name}_{counter}"
            counter += 1
        
        return folder_path
        
    def update_progress(self):
        try:
            while True:
                item = self.progress_queue.get_nowait()
                
                if item[0] == "progress":
                    self.progress_var.set(item[1])
                    self.status_var.set(item[2])
                    
                elif item[0] == "result":
                    self.result_text.insert(tk.END, item[1] + "\n")
                    self.result_text.see(tk.END)
                    
                elif item[0] == "complete":
                    success_count, total_count = item[1], item[2]
                    self.progress_var.set(100)
                    self.status_var.set(f"压缩完成！成功: {success_count}/{total_count}")
                    self.compress_button.config(state="normal")
                    messagebox.showinfo("完成", f"压缩完成！\n成功: {success_count}/{total_count}")
                    return
                    
                elif item[0] == "error":
                    self.status_var.set(f"压缩出错: {item[1]}")
                    self.compress_button.config(state="normal")
                    messagebox.showerror("错误", f"压缩过程中出现错误:\n{item[1]}")
                    return
                    
        except queue.Empty:
            pass
        
        self.root.after(100, self.update_progress)
        
    def run(self):
        self.root.mainloop()

def main():
    try:
        app = ImageCompressorGUI()
        app.run()
    except ImportError as e:
        if "tkinterdnd2" in str(e):
            print("缺少tkinterdnd2依赖，正在安装...")
            os.system("pip install tkinterdnd2")
            print("请重新运行程序")
        else:
            print(f"导入错误: {e}")

if __name__ == "__main__":
    main()